package com.yixun.qfbao.api.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yixun.qfbao.api.service.DueRemainderConfigService;
import com.yixun.qfbao.api.service.ThirdPartyApiService;
import com.yixun.qfbao.api.service.WechatTemplateMessageService;
import com.yixun.qfbao.config.H5UrlConfig;
import com.yixun.qfbao.constant.WxConstant;
import com.yixun.qfbao.constant.WxMessageTemplate;
import com.yixun.qfbao.mapper.DueRemainderConfigMapper;
import com.yixun.qfbao.mapper.SubscriptionMapper;
import com.yixun.qfbao.mapper.WxmsgFailureMapper;
import com.yixun.qfbao.mapper.WxmsgModelMapper;
import com.yixun.qfbao.model.dto.DueRemainderConfigDto;
import com.yixun.qfbao.model.dto.WechatTemplateMsg;
import com.yixun.qfbao.model.dto.WxmsgFailureDto;
import com.yixun.qfbao.model.enums.DelFlagEnum;
import com.yixun.qfbao.model.enums.SendStatusEnum;
import com.yixun.qfbao.model.enums.SystemIdEnum;
import com.yixun.qfbao.service.PartnerInfoService;
import com.yixun.qfbao.util.*;
import com.yixun.qfbao.xf.service.CustomerPremisesService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * User: zilong.liu
 * Date: 2020/7/15
 * Desc:
 */
@Service
public class DueRemainderConfigServiceImpl implements DueRemainderConfigService {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(DueRemainderConfigServiceImpl.class);

    @Autowired
    DueRemainderConfigMapper dueRemainderConfigMapper;
    
    @Autowired
    CustomerPremisesService customerPremisesService;
    
    @Autowired
    DueRemainderConfigService dueRemainderConfigService;
    
    @Autowired
    SubscriptionMapper subscriptionMapper;
    
    @Autowired
    WxAccessTokenCacheUtils wxAccessTokenCacheUtils;
    
    @Autowired
    ThirdPartyApiService thirdPartyApiService;
    
    @Autowired
    PartnerInfoService partnerInfoService;
    
    @Autowired
    H5UrlConfig h5UrlConfig;
    
    @Autowired
    WxmsgModelMapper wxMsgModelMapper;
    
    @Autowired
    WechatTemplateMessageService wechatTemplateMessageService;
    
    @Autowired
    WxmsgFailureMapper wxmsgFailureMapper;
    
    /**
     * 获取消息过期提醒配置的集合
     * @param dueRemainderConfigDto
     */
    @Override
    public List<DueRemainderConfigDto> getDueRemainderConfigList(DueRemainderConfigDto dueRemainderConfigDto) {
        return dueRemainderConfigMapper.getList(dueRemainderConfigDto);
    }

    /**
     * 批量插入消息过期推送配置
     * @param dueRemainderConfigDto
     * @return
     */
    @Override
    public int insertDueRemainderConfig(DueRemainderConfigDto dueRemainderConfigDto) {
        return dueRemainderConfigMapper.insertDueRemainderConfig(dueRemainderConfigDto);
    }

    /**
     * 批量更改消息过期推送配置
     * @param dueRemainderConfigDto
     * @return
     */
    @Override
    public int updateDueRemainderConfig(DueRemainderConfigDto dueRemainderConfigDto) {
        return dueRemainderConfigMapper.updateDueRemainderConfig(dueRemainderConfigDto);
    }

    /**
     *		即将转化为无效的消息提醒
     *			添加报备保护期到期提醒
     *			到访保护期到期提醒
     *			超时未到访转无效提醒
     *			超时未交易转无效提醒
     */
	@Override
	public void sendToBeDueRemainderInfo() {
		
		//获取在配置内过期的消息，以及设置时间基准分次数发送
		int begin = 0;
		int limit = 20;
		Date date = new Date();
		for(int i = 0; i < Integer.MAX_VALUE; i ++) {
			List<Map<String, Object>> dueRemainderInfos = new ArrayList<>();
			dueRemainderInfos = getTobeDueRemainderInfo(begin, limit, date);
			
			if(dueRemainderInfos == null || dueRemainderInfos.size() == 0) {
				LOGGER.info("暂无即将过期的消息，发送结束！");
				return;
			}
			//判断是否要发送微信消息
			judgeSendWxMsg(dueRemainderInfos);
			
			//微信下发,并保存微信记录
			sendWxMessage(dueRemainderInfos);
			
			begin+= limit;
		}
	}
	
	/**
	 * 获取待通知的即将无效的消息
	 */
	private List<Map<String, Object>> getTobeDueRemainderInfo(int begin, int limit, Date date) {
		List<Map<String, Object>> dueRemainderInfos = customerPremisesService.getToBeDueRemainderInfo(begin, limit, date);
		return dueRemainderInfos;
	}
	
	/**
	 * 判断是否要发送微信消息，返回需要发送的集合
	 * @param dueRemainderInfos
	 */
	private void judgeSendWxMsg(List<Map<String, Object>> dueRemainderInfos) {
		
		// 判断无效的公司token，并筛选掉对应的过期记录，应该只需要进行token上的筛选，配置上的筛选，在查询的时候已经筛选过了。
		judgeToken(dueRemainderInfos);
		
		//判断该公司是否配置并开启了微信模板
		Map<String, Object> companyAndMsgTypeInfo = new HashMap<>();
		List<Object> companyList = new ArrayList<>();
		dueRemainderInfos.stream()
			.filter((dueRemainderInfo) -> !companyList.contains(dueRemainderInfo.get("companyId")))
			.forEach((dueRemainderInfo) -> companyList.add(dueRemainderInfo.get("companyId")));
		companyAndMsgTypeInfo.put("msgType", WxMessageTemplate.VISIT_SUCCESS_TEMPLATE_ID);
		companyAndMsgTypeInfo.put("companyList", companyList);
		judgeConfigureMsgModle(companyAndMsgTypeInfo, dueRemainderInfos);
		
		//判断当前员工是否具有wxopen_id
		judgeStaffWxOpenId(dueRemainderInfos);
		
	}
	
	/**
	 * 公司公司ID的集合，判断有哪些公司可能要token无效
	 * @param companyList
	 */
	private void judgeToken(List<Map<String, Object>> dueRemainderInfos) {
		// 1、筛选出重复的公司
		List<Object> companyList = new ArrayList<>();
		dueRemainderInfos.stream()
			.filter((dueRemainderInfo) -> !companyList.contains(dueRemainderInfo.get("companyId")))
			.forEach((dueRemainderInfo) -> companyList.add(dueRemainderInfo.get("companyId")));
		// 2、 获取公司的APPID，secret信息 
		List<Map<String, Object>> subscriptionList = subscriptionMapper.getAppIdAndSecretByCompanyId(companyList);
		// 3、轮流 获取token
		for (Map<String, Object> subscription : subscriptionList) {
			// todo 缓存刷掉造成的token失效问题，目前把最后一次获取的token更新到缓存中，以后若拆分再做考虑
			String companyUuid = String.valueOf(subscription.get("company_uuid"));
			String appId = String.valueOf(subscription.get("app_id"));
			String appSecret = String.valueOf(subscription.get("app_secret"));
	        String accessToken = getAccessToken(companyUuid, appId, appSecret);
			// 4、 筛检掉生成无效token的记录,并把accessToken放置到未被筛选掉的信息中
	        
        	Object companyId = subscription.get("company_id");
        	String companyName = String.valueOf(subscription.get("companyName"));
//	        	Long companyId = subscription.getCompanyId();
        	//遍历不需要发送的消息，如果需要发送塞入access_token
        	for (Map<String, Object> dueRemainderInfo: dueRemainderInfos) {
        		// 如果没有生成token，则移除对应的公司
        		if (StringUtils.isBlank(accessToken) && String.valueOf(companyId).equals(String.valueOf(dueRemainderInfo.get("companyId")))) {
    				dueRemainderInfos.remove(dueRemainderInfo);
        		}else if(StringUtils.isNotBlank(accessToken) && String.valueOf(companyId).equals(String.valueOf(dueRemainderInfo.get("companyId")))) {
        			dueRemainderInfo.put("accessToken", accessToken);
        			dueRemainderInfo.put("companyName", companyName);
        			dueRemainderInfo.put("companyUuid", companyUuid);
        		}
        	}
		}
	}

	/**
	 * 获取token
	 * @param subscription
	 */
	private String getAccessToken(String companyUuid, String appId, String appSecret) {
		String systemId = SystemIdEnum.ASSISTANT.getCode();
        String accessToken = wxAccessTokenCacheUtils.get(companyUuid, systemId);
        LOGGER.info("accessToken redis companyUuid = " + companyUuid + "accessToken redis value = " + accessToken);
        if(StringUtils.isNotBlank(accessToken)) {
        	return accessToken;
        }
        String url = WxConstant.ACCESS_TOKEN_URL;
        url = url.replace("APPID", appId);
        url = url.replace("APPSECRET", appSecret);
		LOGGER.info("get token url = " + url);
        PorxyResult porxyResult = PorxyUtil.getPorxy(url);
        JSONObject resultJson = JSONObject.parseObject(porxyResult.getContext());
        LOGGER.info("token result json, {}", JSONObject.toJSONString(resultJson));
		LOGGER.info("access_token变更: 公司Uuid = " + companyUuid + ", access_token = " + resultJson.getString("access_token") + ", 变更时间 = " + DateUtils.DateToString(Calendar.getInstance().getTime(), DateStyle.YYYY_MM_DD_HH_MM_SS));
		accessToken = resultJson.getString("access_token");
//        accessToken = thirdPartyApiService.getAccessTokenDefault(null, companyUuid, systemId);
        LOGGER.info("get token end .......,token value = " + accessToken);
        if(StringUtils.isNotBlank(accessToken)) {
            // todo 缓存刷掉造成的token失效问题，目前把最后一次获取的token更新到缓存中，以后若拆分再做考虑
        	wxAccessTokenCacheUtils.put(companyUuid, SystemIdEnum.AGENCY_h5.getCode(), accessToken);
        	wxAccessTokenCacheUtils.put(companyUuid, SystemIdEnum.ASSISTANT.getCode(), accessToken);
        }
        return accessToken;
	}
	
	/**
	 *      判断员工的微信ID, 并把员工信息填充进集合
	 * @param dueRemainderInfos
	 */
	private void judgeStaffWxOpenId (List<Map<String, Object>> dueRemainderInfos) {
		// 1、筛选出重复的公司
		List<Object> staffList = new ArrayList<>();
		dueRemainderInfos.stream()
			.filter((dueRemainderInfo) -> !staffList.contains(dueRemainderInfo.get("staffId")))
			.forEach((dueRemainderInfo) -> staffList.add(dueRemainderInfo.get("staffId")));
		
		// 2、根据staffId获取员工部门以及wx_openid
		List<Map<String, Object>> staffInfoList = partnerInfoService.getPartenerInfo(staffList);
		for (Map<String, Object> staffInfo : staffInfoList) {
			//遍历去除没有wxOpenId的用户，并且拼接进去公司部门员工姓名，员工号码信息
			Object staffId = staffInfo.get("id");
			String wxOpenId = String.valueOf(staffInfo.get("wxOpenid"));
			for (Map<String, Object> dueRemainderInfo : dueRemainderInfos) {
				if(String.valueOf(staffId).equals(String.valueOf(dueRemainderInfo.get("staffId")))) {
					//如果存在wxOpenId
					if(!"".equals(wxOpenId) && !"null".equals(wxOpenId)) {
						dueRemainderInfo.put("accountName", staffInfo.get("account_name"));
						dueRemainderInfo.put("phone", staffInfo.get("phone"));
						dueRemainderInfo.put("wxOpenId", wxOpenId);
//						dueRemainderInfo.put("accountName", staffInfo.get("account_name"));
						String departmentName = String.valueOf(staffInfo.get("departmentName"));
						String storeName = String.valueOf(staffInfo.get("storeName"));
						if(!"".equals(departmentName) && !"null".equals(departmentName)) {
							dueRemainderInfo.put("departmentName", departmentName);
							dueRemainderInfo.put("storeName", storeName);
						}else {
							dueRemainderInfo.put("departmentName", storeName);
						}
					}else {
						dueRemainderInfos.remove(dueRemainderInfo);
					}
				}
			}
		}
	}
	
	/**
	 *  判断是否配置了消息模板
	 * @param companyAndMsgTypeInfo
	 * @param dueRemainderInfos
	 */
	private void judgeConfigureMsgModle(Map<String, Object> companyAndMsgTypeInfo, List<Map<String, Object>> dueRemainderInfos) {
		List<Map<String, Object>> msgModels = wxMsgModelMapper.getConfigureMsgModle(companyAndMsgTypeInfo);
		for (Map<String, Object> msgModel : msgModels) {
			Object companyId = msgModel.get("companyId");
			for (Map<String, Object> dueRemainderInfo : dueRemainderInfos) {
				if(String.valueOf(companyId).equals(String.valueOf(dueRemainderInfo.get("companyId")))) {
					String wxModelmsgId = String.valueOf(msgModel.get("wxModelmsgId"));
					//如果存在wxOpenId
					if(!"".equals(wxModelmsgId) && !"null".equals(wxModelmsgId)) {
						dueRemainderInfo.put("wxModelmsgId", wxModelmsgId);
					}else {
						dueRemainderInfos.remove(dueRemainderInfo);
					}
				}
			}
		}
	}
	
//	private String assistantCusPreDetailUrl = "?companyUUid=COMPANYUUID#/customerDetails/CUSPREID/templateMessage";
	/**
	 * 发送微信消息
	 */
	private void sendWxMessage(List<Map<String, Object>> dueRemainderInfos) {
		List<WxmsgFailureDto> wfDtos = new ArrayList<>();
		for (Map<String, Object> dueRemainderInfo : dueRemainderInfos) {
			TreeMap<String, HashMap<String, String>> params = new TreeMap<>();
			String staffId = String.valueOf(dueRemainderInfo.get("staffId"));
			String remainderTypaName = String.valueOf(dueRemainderInfo.get("remainderTypaName"));
			String accessToken = String.valueOf(dueRemainderInfo.get("accessToken"));
			String cusName = String.valueOf(dueRemainderInfo.get("cusName"));
			String cusMobile = String.valueOf(dueRemainderInfo.get("cusMobile"));
			String premiseName = String.valueOf(dueRemainderInfo.get("premiseName"));
			String companyUuid = String.valueOf(dueRemainderInfo.get("companyUuid"));
			String companyName = String.valueOf(dueRemainderInfo.get("companyName"));
			String remainderTime = String.valueOf(dueRemainderInfo.get("remainderTime"));
			String wxOpenId = String.valueOf(dueRemainderInfo.get("wxOpenId"));
			String wxModelmsgId = String.valueOf(dueRemainderInfo.get("wxModelmsgId"));
			String remark = String.valueOf(dueRemainderInfo.get("remark")).replaceAll("remainderTime", remainderTime.substring(0, remainderTime.length()-2));
	        params.put("first", WechatTemplateMsg.item(remainderTypaName));
	        params.put("keyword2", WechatTemplateMsg.item(premiseName));
	        params.put("keyword1", WechatTemplateMsg.item(cusName + " " + cusMobile));
	        params.put("keyword3", WechatTemplateMsg.item(remainderTypaName));
	        params.put("keyword4", WechatTemplateMsg.item(companyName));
	        params.put("remark", WechatTemplateMsg.item(remark));
	        WechatTemplateMsg wechatTemplateMsg = new WechatTemplateMsg();
	        wechatTemplateMsg.setTemplate_id(wxModelmsgId);
	        wechatTemplateMsg.setTouser(wxOpenId);
//	        String url = h5UrlConfig.getWxTemplateMsgAssistantUrl() +
//	        		assistantCusPreDetailUrl.replace("COMPANY_UUID", companyUuid).replace("CUS_PRE_ID", staffId);
	        wechatTemplateMsg.setUrl(null);
	        wechatTemplateMsg.setData(params);
	        String data = JSON.toJSONString(wechatTemplateMsg);
	        TemplateMessageResult result = wechatTemplateMessageService.sendTemplate(accessToken, data);
	        
	        //组装微信记录消息
	        WxmsgFailureDto wfDto = buildWXFailureMsg(companyUuid, SystemIdEnum.AGENCY_WEB.getCode(), wxOpenId, data, 
	        		String.valueOf(WxMessageTemplate.REPORT_CHECK_TEMPLATE_ID), 
	        		wxModelmsgId, result, Long.parseLong(staffId));
	        wfDtos.add(wfDto);
		}
		
		wxmsgFailureMapper.saveWfDtos(wfDtos);
		
	}
	
	/**
	 * 	构建短信记录信息
	 * @param companyUuid
	 * @param systemId
	 * @param openId
	 * @param content
	 * @param wxmodelType
	 * @param wxmodelUuid
	 * @param result
	 * @param cusPreid
	 */
	private WxmsgFailureDto buildWXFailureMsg(String companyUuid, String systemId, String openId, String content, 
			String wxmodelType, String wxmodelUuid, TemplateMessageResult result, Long cusPreid){
        WxmsgFailureDto wfDto = new WxmsgFailureDto();
        if(StringUtils.isEmpty(companyUuid)){
            return null;
        }
        wfDto.setCompanyUuid(companyUuid);
        wfDto.setSystemId(systemId);
        wfDto.setReceiveOpenid(openId);
        wfDto.setCusPreid(cusPreid);
        wfDto.setReceiveContent(content);
        wfDto.setWxmodelType(wxmodelType);
        wfDto.setWxmodelUuid(wxmodelUuid);
		wfDto.setIsResend(false);
        if(!"ok".equals(result.getErrmsg().toLowerCase())) {
        	//根据之前的SQL 判定是否进行重发机制  result like '%40001%' OR result like '%system error%'
        	if (40001 == result.getErrcode() || StringUtils.contains(result.getErrmsg(),"system error")){
				wfDto.setSendStatus(SendStatusEnum.RESEND.getNo());
				wfDto.setIsResend(true);
			}else {
				wfDto.setSendStatus(SendStatusEnum.FAILURE.getNo());
			}
        }else{
            wfDto.setSendStatus(SendStatusEnum.SUCCESS.getNo());
        }
        wfDto.setResult(JSONObject.toJSONString(result));
        wfDto.setDelFlag(DelFlagEnum.UNDEL.getNo());
        wfDto.setCreateTime(Calendar.getInstance().getTime());
        return wfDto;
    }
	
	/**
	 * 发送已经到期的微信提醒消息
	 */
	public void sendDueRemainderInfo() {
		//获取在配置内过期的消息，分次数发送。
		int begin = 0;
		int limit = 20;
		Date date = new Date();
		for(int i = 0; i < Integer.MAX_VALUE; i ++) {
			List<Map<String, Object>> dueRemainderInfos = new ArrayList<>();
			dueRemainderInfos = getDueRemainderInfo(begin, limit, date);
			
			if(dueRemainderInfos == null || dueRemainderInfos.size() == 0) {
				LOGGER.info("暂无过期的消息，发送结束！");
				return;
			}
			//判断是否要发送微信消息
			judgeSendWxMsg(dueRemainderInfos);
			
			//微信下发,并保存微信记录
			sendWxMessage(dueRemainderInfos);
			
			//更改当前发送次数为1
			addSendTimesById(dueRemainderInfos);
			
			begin+= limit;
		}

	}
	
	/**
	 * 获取已经无效的通知消息
	 */
	private List<Map<String, Object>> getDueRemainderInfo(int begin, int limit, Date date) {
		List<Map<String, Object>> dueRemainderInfos = customerPremisesService.getDueRemainderInfo(begin, limit, date);
		return dueRemainderInfos;
	}
	
	/**
	 * 根据ID和remainderType，add状态变更提醒次数
	 * @param dueRemainderInfos
	 */
	private void addSendTimesById(List<Map<String, Object>> dueRemainderInfos) {
		customerPremisesService.addSendTimesById(dueRemainderInfos);
	}
	
}
